package xyz.xtt.admin.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.BatchResult;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import xyz.xtt.admin.constants.*;
import xyz.xtt.admin.entity.*;
import xyz.xtt.admin.listener.AssetHandleEvent;
import xyz.xtt.admin.mapper.AssetMapper;
import xyz.xtt.admin.mapper.UserMapper;
import xyz.xtt.admin.request.AssetRequest;
import xyz.xtt.admin.request.GiftRequest;
import xyz.xtt.admin.service.IActivityLogService;
import xyz.xtt.admin.service.IActivityService;
import xyz.xtt.admin.service.IAssetService;
import org.springframework.stereotype.Service;
import xyz.xtt.admin.service.IUserService;
import xyz.xtt.admin.utils.ExcelUtils;
import xyz.xtt.admin.utils.MyAESUtil;
import xyz.xtt.admin.vo.*;
import xyz.xtt.asset.client.AssetClient;
import xyz.xtt.asset.client.request.MintRequest;
import xyz.xtt.asset.client.request.TransferFromRequest;
import xyz.xtt.asset.client.response.MintResponse;
import xyz.xtt.asset.client.response.TransferFromResponse;
import xyz.xtt.common.objects.DozerUtil;
import xyz.xtt.common.response.ApiResult;
import xyz.xtt.common.starter.cache.CacheManager;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;

/**
 * <p>
 * 资产基础信息表 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-21
 */
@Slf4j
@Service
public class AssetServiceImpl extends ServiceImpl<AssetMapper, TblAsset> implements IAssetService {

    @Autowired
    private AssetMapper assetMapper;

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private IUserService userService;

    @Autowired
    private AssetClient assetClient;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private IActivityService activityService;
    @Autowired
    private IActivityLogService activityLogService;

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    private boolean checkVal(AssetRequest assetRequest, Function<AssetRequest, String> getAssetName) {
        return Optional.ofNullable(assetRequest).map(getAssetName).map(StringUtils::isNotBlank).orElse(false);
    }

    @Override
    public Page<AssetListVo> getList(@NotNull AssetRequest assetRequest) throws Exception {
        MPJLambdaWrapper<TblAsset> query = JoinWrappers.lambda(TblAsset.class)
                .leftJoin(TblAssetExt.class, TblAssetExt::getAssetId, TblAsset::getId)
                .selectAll(TblAsset.class);
        // 根据手机号查询 数据
        if (!assetRequest.getAuthor_name().isEmpty() || !assetRequest.getAuthor_phone().isEmpty()) {
            TblUser user = userService.getUserByNameAndPhone(assetRequest.getAuthor_name(), assetRequest.getAuthor_phone());
            if (user == null) {
                return null;
            }
            //author_id
            query.eq(TblAsset::getAuthorId, user.getAppUserId());
        }

        if (!assetRequest.getOwner_name().isEmpty() || !assetRequest.getOwner_phone().isEmpty()) {
            TblUser user = userService.getUserByNameAndPhone(assetRequest.getOwner_name(), assetRequest.getOwner_phone());
            if (user == null) {
                return null;
            }
            query.eq(TblAsset::getOwnerId, user.getAppUserId());
        }

        if (!assetRequest.getId().isEmpty()) {
            Long id = Long.parseLong(assetRequest.getId());
            query.eq(TblAsset::getId, id);
        }
        if (!assetRequest.getAsset_name().isEmpty()) {
            query.like(TblAsset::getAssetName, assetRequest.getAsset_name());
        }
        if (!assetRequest.getGoods_id().isEmpty()) {
            query.eq(TblAsset::getGoodsId, assetRequest.getGoods_id());
        }
        if (assetRequest.getFirst_type() != null) {
            query.eq(TblAsset::getFirstType, assetRequest.getFirst_type());
        }
        query.eq(Objects.nonNull(assetRequest.getUpChain()), TblAssetExt::getUpChain, assetRequest.getUpChain())
                .orderByDesc(TblAsset::getCreateDatetime);
        Page<TblAsset> page = assetMapper.selectPage(new Page<>(assetRequest.getCurrent(), assetRequest.getPageSize()), query);

        Set<String> userIds = new HashSet<>();

        // 获取对应UserID列表
        page.getRecords().forEach(x -> {
            userIds.add(x.getAuthorId());
            userIds.add(x.getOwnerId());
        });

        List<AssetListVo> assetListVos = page.getRecords().stream().map(this::setAssetListVo).toList();
        // 获取ID对应用户列表
        Map<String, TblUser> userList = userService.getUserListByAppUserIds(new ArrayList<>(userIds));
        List<AssetListVo> list = assetListVos.stream().peek(x -> {
            if (userList.get(x.getAuthorId()) != null) {
                x.setAuthorName(userList.get(x.getAuthorId()).getNickname());
            }
            if (userList.get(x.getOwnerId()) != null) {
                x.setOwnerName(userList.get(x.getOwnerId()).getNickname());
            }
        }).toList();

        Page<AssetListVo> objectPage = new Page<>();
        objectPage.setRecords(list)
                .setCurrent(page.getCurrent())
                .setTotal(page.getTotal())
                .setSize(page.getSize())
                .setPages(page.getPages());
        return objectPage;
    }


    //设置返回的数据
    public AssetListVo setAssetListVo(TblAsset tblAsset) {

        AssetListVo assetListVos = DozerUtil.transfor(tblAsset, AssetListVo.class);
        assetListVos
                //.setFirstType(AssetTypeEnum.queryConvert(tblAsset.getFirstType()).desc())
                .setUnit(PayUnitEnum.queryConvert(tblAsset.getUnit()) == null ? "" : PayUnitEnum.queryConvert(tblAsset.getUnit()).desc())
                .setShowState(ShowStateEnum.queryConvert(tblAsset.getShowState()).desc())
                .setSellPattern(tblAsset.getSellPattern());
        return assetListVos;
    }


    @Override
    public AssetVo get(Long id) {
        AssetVo info = assetMapper.getInfo(id);
        if (info == null) {
            return null;
        }
        List<String> appUserIds = new ArrayList<>();
        appUserIds.add(info.getAuthorId());
        appUserIds.add(info.getOwnerId());
        Map<String, TblUser> userListByAppUserIds = userService.getUserListByAppUserIds(appUserIds);


        info
                //.setFirstTypeName(AssetTypeEnum.queryConvert(info.getFirstType()).desc())
                .setSecondTypeName(Optional.ofNullable(GroundTypeEnum.queryConvert(info.getSecondType())).
                        map(GroundTypeEnum::desc).orElse(""))
                .setShowStateType(ShowStateEnum.queryConvert(info.getShowState()).desc())
                .setSellPatternType(SellPatternEnum.queryConvert(info.getSellPattern()).desc())
                .setFileTypeName(FileTypeEnum.queryConvert(info.getFileType()).desc());
        if (userListByAppUserIds.get(info.getAuthorId()) != null) {
            info.setAuthorName(userListByAppUserIds.get(info.getAuthorId()).getNickname());
        }
        if (userListByAppUserIds.get(info.getOwnerId()) != null) {
            info.setOwnerName(userListByAppUserIds.get(info.getOwnerId()).getNickname());
        }
        return info;
    }


    // 作废
    public Page<AssetListVo> getListV2(AssetRequest assetRequest) {
        // 请求数据库
        LambdaQueryChainWrapper<TblAsset> query = new LambdaQueryChainWrapper<>(baseMapper);
        Page<TblAsset> page = query.eq(null != assetRequest.getId(), TblAsset::getId, assetRequest.getId())
                .eq(null != assetRequest.getGoods_id(), TblAsset::getGoodsId, assetRequest.getGoods_id())
                .eq(null != assetRequest.getAsset_name(), TblAsset::getAssetName, assetRequest.getAsset_name())
                .page(new Page<>(assetRequest.getCurrent(), assetRequest.getPageSize()));
        // 重置字段
        List<AssetListVo> assetListVos = DozerUtil.transforList(page.getRecords(), AssetListVo.class);
        Page<AssetListVo> objectPage = new Page<>();
        objectPage.setRecords(assetListVos)
                .setCurrent(page.getCurrent())
                .setTotal(page.getTotal())
                .setSize(page.getSize())
                .setCurrent(page.getCurrent())
                .setPages(page.getPages());
        return objectPage;
    }


    public Boolean seAssetSellPattern(Long id, String goodsId, Integer status) {
        LambdaUpdateChainWrapper<TblAsset> updateWrapper = new LambdaUpdateChainWrapper<>(baseMapper);
        updateWrapper.eq(TblAsset::getId, id)
                .set(TblAsset::getSellPattern, status);
        boolean res = updateWrapper.update();
        // 删除缓存
        cacheManager.delete(ExchangeCache.TBL_ASSET, goodsId);
        return res;
    }

    @Override
    public MintResponse mint(MintRequest mintRequest) {
        mintRequest.setBizId(String.valueOf(IdWorker.getId("mintNFT")));
        mintRequest.setBizType("nft");
        mintRequest.setAssetType("NFT");
        mintRequest.setReferProtocol("1155");
        ApiResult<MintResponse> mint = assetClient.mint(mintRequest.getAuthorId(), mintRequest);
        return mint.getData();
    }

    @Override
    public Boolean transferFrom(GiftRequest giftRequest) {
        TblAsset tblAsset = assetMapper.selectById(giftRequest.getFrom());
        if (Objects.isNull(tblAsset)) {
            log.error("[transferFrom] From资产不存在");
            return false;
        }
        MultipartFile file = giftRequest.getFile();
        // 使用 EasyExcel 读取 MultipartFile 中的数据
        List<TblUser> userByPhone = userService.getUserByPhone(readExcel(file));
        userByPhone.forEach(user -> {
            TransferFromRequest transferFromRequest = TransferFromRequest
                    .builder()
                    .from(giftRequest.getFrom())
                    .to(user.getAppUserId())
                    .amount(Math.max(giftRequest.getAmount(), 1))
                    .price(tblAsset.getPrice().stripTrailingZeros().toPlainString())
                    .bizId(tblAsset.getGoodsId())
                    .orderType("GIFT")
                    .orderNo(IdUtil.getSnowflakeNextIdStr())
                    .fees(BigDecimal.ZERO)
                    .build();
            ApiResult<TransferFromResponse> transferFromResponseApiResult = assetClient.transferFrom("nft", transferFromRequest);
            if (!transferFromResponseApiResult.isOk()) {
                log.error("[transferFrom] 资产转让失败, phone is " + user.getPhone());
            }
        });

        return null;
    }

    private List<String> readExcel(MultipartFile file) {
        List<String> phoneNumbers = new ArrayList<>();
        try {
            ExcelDataListener excelDataListener = new ExcelDataListener();
            EasyExcel.read(file.getInputStream(), ExcelData.class, excelDataListener)
                    .sheet()
                    .doRead();
            List<ExcelData> dataList = excelDataListener.getDataList();
            System.out.println(dataList);
            dataList.forEach(data -> {
                // 用正则校验手机号格式
                if (org.springframework.util.StringUtils.hasText(data.getText()) && data.getText().matches("^1[3-9]\\d{9}$")) {
                    phoneNumbers.add(data.getText());
                } else {
                    System.out.println("手机号格式错误" + data.getText());
                }
            });
        } catch (Exception e) {
            log.error("[readExcel] 读取文件失败", e);
        }
        return phoneNumbers;
    }

    /**
     * 资产回收,参数file可以为空【兼容批量处理】
     */
    public Integer assetReuse(AssetHandleVo handleVo, MultipartFile file) {
        return 0;
    }

    /**
     * 资产空投批量,参数file不可为空
     */
    @Transactional
    public Integer assetDrop(AssetHandleVo handleVO, MultipartFile file) throws Exception {
        Assert.notNull(file);
        // 活动是否存在
        TblActivity activity = activityService.getOneById(handleVO.getActivityId());
        Assert.notNull(activity);
        // 读取file
        List<ExcelVO> readList = ExcelUtils.readExcel(file, ExcelVO.class);
        log.info("this time,{} files read from upload file", readList.size());
        // 查库校验用户状态【存在与否、是否注销】输出总数
        List<TblActivityLog> insertList = makeActivityLog(readList, activity);
        Assert.isTrue(!insertList.isEmpty());
        log.info("obtain  {} records normal data", insertList.size());
        // 正常的数据入库，等待资产划转
        int count = 0;
        List<BatchResult> batchResults = activityLogService.batchSave(insertList);
        for (BatchResult result : batchResults) {
            count += result.getUpdateCounts().length;
        }
        log.info("success insert {} records normal data to mysql", count);
        // 发送空投资产事件
        eventPublisher.publishEvent(new AssetHandleEvent<TblActivityLog, TblActivity>(this, handleVO.getMethod(), activity, insertList));
        return count;
    }

    /**
     * 检查提交的数据
     */
    @NotNull
    private List<TblActivityLog> makeActivityLog(List<ExcelVO> readList, TblActivity activity) throws Exception {
        List<TblActivityLog> insertList = new ArrayList<>();
        for (ExcelVO vo : readList) {
            String mobile = String.valueOf(vo.getPhone()).trim();
            Assert.isTrue(PhoneUtil.isMobile(mobile));
            mobile = MyAESUtil.encrypt("86" + mobile);
            TblUser user = new TblUser();
            user.setUserName("dropAsset");
            user.setAppUserId(1949L);
            user.setPhone(mobile);
            insertList.add(createData(user, activity));
            // 标识数据状态：0、未处理数据，1、正常数据
            vo.setStatus(1);
        }
        return insertList;
    }

    /**
     * 构建待空投数据
     */
    TblActivityLog createData(TblUser u, TblActivity act) {
        Snowflake secondSnow = IdUtil.getSnowflake(1, 1);
        return TblActivityLog.builder()
                .id(secondSnow.nextId())
                .activityId(act.getId())
                .userId(u.getAppUserId())
                .userName(u.getNickname())
                .userPhone(u.getPhone())
                .assetId(act.getAssetId())
                .status(0)
                .assetName(act.getAssetName())
                .amount(RandomUtil.randomInt(act.getMin(), act.getMax()))
                .type(0)
                .build();
    }


}
